package com.xneure.code.algorithm;

import java.util.Arrays;
import java.util.Iterator;
import com.xneure.utils.ObjectUtil;

public class Descartes<T> implements Iterator<T[]>{
	private int[] domino;
	private boolean isGet = false;
	private boolean possible;
	private T[][] source;
	private T[] result;
	private int terminus;
	private int size = 1;

	@SuppressWarnings("unchecked")
	public Descartes(T[][] element){
		source = element;
		terminus = element.length - 1;
		if(terminus < 1){
			size = 0;
			possible = false;
			isGet = true;
			return;
		}
		result = (T[])ObjectUtil.newArrays(element[0].getClass(),element.length);
		domino = new int[element.length];
		for(int i = 0;i <= terminus;i++){
			result[i] = element[i][domino[i] = 0];
			size *= element[i].length;
		}
	}

	public Descartes(T[][] element,T[] result){
		source = element;
		terminus = element.length - 1;
		this.result = result;
		domino = new int[element.length];
		for(int i = 0;i <= terminus;i++){
			result[i] = element[i][domino[i] = 0];
			size *= element[i].length;
		}
	}

	public boolean hasNext(){
		if(isGet == false){
			isGet = true;
			if(possible = domino[terminus] < source[terminus].length){
				result[terminus] = source[terminus][domino[terminus]++];
			}else{
				if(checkTouch(terminus)){
					result[terminus] = source[terminus][domino[terminus] = 0];
					domino[terminus]++;
				}
			}
		}
		return possible;
	}

	boolean checkTouch(int touch){
		possible = false;
		for(;;){
			if(touch >= 0){
				if(++domino[touch] < source[touch].length){
					result[touch] = source[touch][domino[touch]];
					possible = true;
					break;
				}else{
					domino[touch] = 0;
					result[touch] = source[touch][domino[touch--]];
					if(touch < 0){
						break;
					}
				}
			}else{
				break;
			}
		}
		return possible;
	}

	public int getSize(){
		return size;
	}

	public T[] next(){
		if(isGet){
			isGet = false;
		}
		return result;
	}

	public T[] nextCopy(){
		T[] next = next();
		return Arrays.copyOf(next,next.length);
	}

	@Override
	public void remove(){
	}
}
